package com.hydra.base.query;

import com.hydra.base.entity.EsIdEntity;
import com.hydra.base.resp.Pager;
import com.hydra.base.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * ES的查询建造器模式
 * @Author 陈煜敏
 * @Date 2022/9/16 13:36
 */
@Slf4j
public class EsQueryBuilder<T extends EsIdEntity> {

    private final BoolQueryBuilder queryBuilder;
    private final Class<T> tClass;
    private int pageNo;
    private int pagerSize;
    private final List<Sort.Order> orders;

    private QueryHandler handler;

    private final ElasticsearchOperations elasticsearchOperations;

    public EsQueryBuilder(Class<T> tClass){
        this.tClass = tClass;
        this.orders = new ArrayList<>();
        this.queryBuilder = QueryBuilders.boolQuery();
        this.elasticsearchOperations = SpringUtils.getBean(ElasticsearchOperations.class);
    }

    /**term查询*/
    public EsQueryBuilder<T> termQuery(String prop, String value){
        queryBuilder.must(QueryBuilders.termQuery(prop, value.trim()));
        return this;
    }

    /**term查询数组*/
    public EsQueryBuilder<T> termQuery(String prop, String[] value){
        queryBuilder.must(QueryBuilders.termQuery(prop, value));
        return this;
    }

    /**匹配查询*/
    public EsQueryBuilder<T> matchQuery(String prop, String value){
        queryBuilder.must(QueryBuilders.matchQuery(prop, value.trim()));
        return this;
    }

    /**模糊查询*/
    public EsQueryBuilder<T> wildcardQuery(String prop, String value){
        queryBuilder.must(QueryBuilders.wildcardQuery(prop, "*"+ value.trim()+"*"));
        return this;
    }

    /**设置分页下标，从0开始*/
    public EsQueryBuilder<T> setPageNo(int pageNo) {
        this.pageNo = pageNo;
        return this;
    }

    /**设置分页每页数量*/
    public EsQueryBuilder<T> setPagerSize(int pagerSize) {
        this.pagerSize = pagerSize;
        return this;
    }

    public EsQueryBuilder<T> sortBy(String prop, boolean desc) {
        Sort.Direction direction = desc? Sort.Direction.DESC :Sort.Direction.ASC;
        orders.add(new Sort.Order(direction, prop));
        return this;
    }

    /**设置自定义的查询处理器*/
    public EsQueryBuilder<T> setQueryHandler(QueryHandler handler){
        this.handler = handler;
        return this;
    }

    /**
     * 构建前端的分页对象
     */
    private Pager<T> buildPager(){
        //分页查询
        Pager<T> pager = new Pager<>();
        if(pagerSize == 0){
            pagerSize = 10;
        }
        pager.setPageNo(pageNo);
        pager.setSize(pagerSize);
        return pager;
    }

    public Pager<T>  queryPager(){
        Pager<T> pager = this.buildPager();
        //排序
        Sort sort = Sort.by(orders);
        //获取页码
        PageRequest pageRequest = PageRequest.of(pageNo, pagerSize);

        //自定义查询器
        if(this.handler != null){
            this.handler.handle(queryBuilder);
        }

        //构建查询对象
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .build()
                .setPageable(pageRequest)
                .addSort(sort);
        log.info(Objects.requireNonNull(searchQuery.getQuery()).toString());

        SearchHits<? extends EsIdEntity> result = elasticsearchOperations.search(searchQuery, tClass);

        log.info("查询结果："+result.getTotalHits());

        List<T> list = new ArrayList<>();
        List<? extends SearchHit<? extends EsIdEntity>> searchHits = result.getSearchHits();
        for (SearchHit<? extends EsIdEntity> searchHit : searchHits) {
            T obj = (T)searchHit.getContent();
            list.add(obj);
        }
        long total = result.getTotalHits();
        pager.setTotal(total);
        int totalPages = (int)Math.ceil(((double)total)/pagerSize);
        pager.setTotalPages(totalPages);
        pager.setRecords(list);
        return pager;
    }

}
